#!/usr/bin/env perl
#
#	This perl script can be used to maintain an up-to-date builds of 
#	Chaos libraries on non-CoC machines.  It is a subset of the script
#	which does nightly builds on CoC machines, but without Email 
#	notification of failure.  It rebuilds libraries only if 1) they 
#	were not built before, 2) their last build failed, 3) their source
#	changed, or 4) a project that they depend upon was rebuilt.
#	This script is suitable for running from cron.
#	
# Configuration:
#  The following four variables can be customized for your site.
#     $PREFIX is the install directory (lib, include, bin, etc. will be
#	created below $PREFIX.
#    $CVS_ARCHIVE is the CVSROOT to use (change to your username)
#    $BUILD_LOCATION is the place where the build will occur.  This
#	directory should be persistent (I.E. not in /tmp).
#    $PROJECTS_TO_BUILD may change as we add new projects, but should 
#	change rarely.
#
#$HOME=$ENV{'HOME'};
$HOME=$ENV{'PWD'};
$CONFIG_ARGS = "--prefix=$HOME/evpath";
$cmake_install_dir = "$HOME/evpath";
$SVN_ARCHIVE="http://svn.research.cc.gatech.edu/kaos";
$BUILD_LOCATION="$HOME/EVPATH_LIB/nightly_build_area";
#$PROJECTS_TO_BUILD="dill cercs_env gen_thread atl ffs evpath echo2 comm_group cmrpc pds gs soapstone amaze";
$PROJECTS_TO_BUILD="dill cercs_env gen_thread atl ffs evpath";

# Nothing should need to be changed below here.

sub normal_output {
    if (!($quiet != 0)) {
	foreach $foo (@_) {
	    print $foo;
	}
    }
    foreach $foo (@_) {
	print SUMMARY $foo;
    }
}

sub verbose_output {
    if ($verbose != 0) {
	foreach $foo (@_) {
	    print $foo;
	}
    }
    foreach $foo (@_) {
	print SUMMARY $foo;
    }
}


$nocheck++;
( -f "$HOME/.enable_check" ) && ($nocheck = 0);

while ($_ = $ARGV[0], /^-/) {
    shift;
    last if /^--$/;
    if(/^-D(.*)/) { $debug = $1 }
    elsif(/^-v/)    { $verbose++ }
    elsif(/^-q/)    { $quiet++  }
    elsif(/^-no_check/) {$nocheck++}
    else {
	print  "skipping arg $_\n";
    }
  }

$hostname = `hostname`; chop($hostname);

($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isds) 
		    = localtime(time);
$year += 1900;
$now_string = "$mon/$mday/$year $hour:$min:$sec";

verbose_output ("building on host ", $host, " at ", $now_string,
		"\n\n");

foreach $project (split(" ", $PROJECTS_TO_BUILD)) {

    normal_output('Considering project "', $project, '"',"\n");
    chdir $HOME;
    if ( ! -d "$BUILD_LOCATION" ) {
	verbose_output("    Making directory \"$BUILD_LOCATION\"\n");
	mkdir ($BUILD_LOCATION, 0755) || die "Couldn't mkdir $BUILD_LOCATION";
    }
    chdir "$BUILD_LOCATION";
    if (! -d "$project") {
	verbose_output("    Making directory \"$project\"\n");
	mkdir ("$project", 0755);
    }	
    $build_dir = $project;

    $build = 0;
    verbose_output("    Build determination\n");
    if (( -f "$HOME/.build_all" ) || (-f "$HOME/.build_$project")) {
	# if there were a .build_all file, don't even think of not building.
	normal_output("    building \"$project\" because of .build_all\n");
	$build = 1;
    } else {
        verbose_output("	No \$HOME/.build_all or \$HOME/.build_$project\n");
	$_ = $HOME;
	
	if ( -r "$project/.last_good_build") {

	    ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
	     $atime,$mtime,$ctime,$blksize,$blocks)
		= stat("$project/.last_good_build");
	    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isds) 
		= localtime($mtime);
	    $mon++;
	    $year += 1900;
	    my $monalt = sprintf("%02u", $mon);
	    my $dayalt = sprintf("%02u", $mday);
	    $strctime = "$year-$monalt-$dayalt";
	    $repo_date = _get_repos_date("${SVN_ARCHIVE}/$project/");
	    verbose_output("Repository rev date is $repo_date\n");
	    ($y,$m,$d) = split("-", (split(" ",$repo_date))[0]);
	    $repo_date = "$y$m$d";
	    $build_date = "$year$monalt$dayalt";
	    if ($build_date <= $repo_date) {
		normal_output("	building \"$project\" because of updated source\n");
		$build = 1;
	    }		
	} else {
	    $build = 1;
	}

    }
    if (! -d "$build_dir") {
	normal_output("	building \"$project\" because it wasn't built here before\n");
	$build = 1;
    }
    $! = 0;
    if (($build == 0) && ( -r "$build_dir/.depends")) {
	$DEPENDS = $build_dir . "/.depends";
	open(DEPENDS, $DEPENDS) || die "open failed on $DEPENDS : $!";
	while (<DEPENDS>) {
	    chop;
	    s/^\s*//g;
	    s/\s*$//g;
	    if (defined($built{$_})) {
		normal_output("	building \"$project\" because \"$_\" was rebuilt\n");
		$build = 1;
		close DEPENDS;
	    } else {
		verbose_output("	depends on \"$_\", but that was not rebuilt\n");
	    }
	}
    } else {
	if ($build == 0) {
	    verbose_output("	no .depends file\n");
	}
    }
    if ($build == 0) {
	normal_output("    Skipping project \"$project\"\n");
	chdir $build_dir || die "Couldn't chdir to $build_dir";
	if ( -r ".keep_source") {
	    system("rm -fr .svn 1> /dev/null 2>&1");
	} else {
	    system("rm -fr * 1> /dev/null 2>&1");
	}
	next;
    }
    $built{$project} = 1;
    system("rm -fr $build_dir");
    $use_svn = 1;
    if ($use_svn) {
#	$command = "svn co -q --username=anon --password=anon ${SVN_ARCHIVE}/$project/trunk $build_dir";
	$command = "svn co -q --username=anon --password=anon ${SVN_ARCHIVE}/$project/trunk $build_dir";
    } else {
	$command = "cvs -Q -d $CVS_ARCHIVE checkout -d $build_dir $project";
    }
    normal_output("    Doing: $command \n");
    system($command);
    chdir $build_dir;
    normal_output('    Building project "', $project, '"', "\n");
    if (! defined($CONFIG_ARGS)) {
	$CONFIG_ARGS = "--prefix=$HOME";
	$cmake_install_dir = $HOME;
    }
    $args = '"'.$CONFIG_ARGS.'"';
    $eval_args = eval($args); warn $@ if $@;
    ( -f "$HOME/.enable_disassembly" ) &&
      ("dill" eq $project) && ($eval_args .= " --enable-disassembly");
    if (($dir) = ($eval_args =~ /.*-bindir=(\S+)\b.*/)) {
	if (! -d $dir) {
	    verbose_output("doing bin mkdir on $dir\n");
	    verbose_output("in was $eval_args\n");
	    mkdir($dir,0777);
	}
    }
    if (($dir) = ($eval_args =~ /.*-libdir=(\S+)\b.*/)) {
	if (! -d $dir) {
	    verbose_output("doing lib mkdir on $dir\n");
	    verbose_output("in was $eval_args\n");
	    mkdir($dir,0777);
	}
    }
    if (($dir) = ($eval_args =~ /.*-includedir=(\S+)\b.*/)) {
	if (! -d $dir) {
	    verbose_output("doing include mkdir on $dir\n");
	    verbose_output("in was $eval_args\n");
	    mkdir($dir,0777);
	}
    }
    unlink ('config.cache');
    unlink ('config.h');
    unlink(".last_good_build");
    $USER = $ENV{"USER"};
    $results="/tmp/build_results_$USER.$project";
    open(RESULTS, ">$results") || die "failed to open $results\n";
    print RESULTS "\nBuilding project $project on $hostname\n\n";
    close(RESULTS);
    %STORE_ENV = %ENV;		# save environment

    foreach $var (split("&", $config_env_string{$repository})) {
	($name,$value) = split("=",$var);
	$ENV{$name} = $value;
    }

    
    if ( -r "./CMakeLists.txt" ) {
	verbose_output("    Running  cmake -DCMAKE_INSTALL_PREFIX=$cmake_install_dir .\n");
	$command = "cmake -DCMAKE_INSTALL_PREFIX=$cmake_install_dir . 1>>$results 2>&1";
    } else {
	verbose_output("    Running ./configure $eval_args \n");
	$command = "./configure $eval_args 1>>$results 2>&1";
    }
    system($command);
    system("make clean 1> /dev/null 2>&1");
    system("make depend 1> /dev/null 2>&1 ");
    if (( -r "Makefile.am") && ($nocheck == 0)) {
	# a make check
	$result = system("make check install 1>>$results 2>&1");
	if (($result / 256) != 0) {
	    print "Make check install failed\n";
	    $failed_projects .= " $project";
	} else {
	    system("touch .last_good_build");
	}
    } elsif (( -r "./CMakeLists.txt") && ($nocheck == 0)) {
	# a make check
	$result = system("make test install 1>>$results 2>&1");
	if (($result / 256) != 0) {
	    print "Make test install failed\n";
	    $failed_projects .= " $project";
	} else {
	    system("touch .last_good_build");
	}
    } else {
	$result = system("make install 1>>$results 2>&1");
	if (($result / 256) != 0) {
	    print "Make install failed\n";
	    $failed_projects .= " $project";
	} else {
	    system("touch .last_good_build");
	}
    }
    if ( ! -r ".no_clean") {
	system("make clean 1> /dev/null 2>&1");
    }
    %ENV = %STORE_ENV;		# restore environment
}

if (!$quiet) {
    if ($failed_projects ne "") {
	print "These projects failed to install : $failed_projects\n";
    }
}

sub _get_repos_date {
  my ($repos) = @_;

  verbose_output("Doing log \"svn info $repos\"\n");

  open my $log, "svn info $repos|"
    or die "couldn't open pipe to svn process: $!";

  my ($date);

  my $daterule = qr/Last Changed Date: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/;

  while (<$log>) {
    if (m/$daterule/) {
	$date = $1;
    }
  }
  close($log);
  return $date;

}
